package util;

import enums.ColumnType;
import enums.ConstraintNameEnum;
import enums.SchemaIndexEnum;

import java.sql.SQLException;
import java.util.*;

/**
 * 获取对比结果
 */
public class DataDiff {

    /**
     * 获取表的对比结果
     * 包含没有添加的表，不同的字段属性
     */
    public static Map<String, Object> diffTablesOrViews(Map<String, Map<String, Map<ColumnType, Object>>> source,
                                                        Map<String, Map<String, Map<ColumnType, Object>>> target) {


        Map<String, Map<String, Object>> diffFields = new HashMap<>();
        List<String> newTables = new ArrayList<>();

        source.forEach((tableName, tableDetails) -> {

            //对比缺少的表
            //table2对比table1没有的表名
            if (!target.containsKey(tableName)) {
                newTables.add(tableName);
                return;
            }
            //对比不同的字段
            Map<String, Object> lackField = diffLackField(tableDetails, target.get(tableName));
            if (!lackField.isEmpty()) {
                diffFields.put(tableName, lackField);
            }
        });

        List<String> dropTables = new ArrayList<>();
        Map<String, Map<String, Object>> deleteFields = new HashMap<>();
        target.forEach((tableName, tableDetails) -> {

            //对比缺少的表
            //table2对比table1没有的表名
            if (!source.containsKey(tableName)) {
                dropTables.add(tableName);
                return;
            }
            //对比不同的字段
            Map<String, Object> lackField = deleteField(tableDetails, source.get(tableName));
            if (!lackField.isEmpty()) {
                deleteFields.put(tableName, lackField);
            }
        });

        Map<String, Object> map = new HashMap<>();
        map.put("newTables", newTables);
        map.put("dropTables", dropTables);
        map.put("diffFields", diffFields);
        map.put("deleteFields", deleteFields);
        return map;
    }

    /**
     * 对比两张表是否相等
     */
    private static Boolean compareFieldVal(Object source, Object target) {
        return (source == null && target == null) || ((source != null && target != null) && (source.equals(target)));
    }


    /**
     * 对比不同的字段
     *
     * @author: haoqi
     * @date: 2020/8/19 4:17 下午
     */
    public static Map<String, Object> diffLackField(Map<String, Map<ColumnType, Object>> source,
                                                    Map<String, Map<ColumnType, Object>> target) {

        Map<String, Object> diffTable = new HashMap<>();
        source.forEach((fieldName, sourceFieldVals) -> {

            /*
             * 对比缺少的字段
             */
            if (!target.containsKey(fieldName)) {
                diffTable.put(fieldName, sourceFieldVals);
                return;
            }


            Map<ColumnType, Object> targetFieldVals = target.get(fieldName);
            //对每一个字段的各个类型进行比较
            for (ColumnType fieldType : ColumnType.columnTypes()) {
                //比对两个表字段中不同的属性
                Object sourceFieldVal = sourceFieldVals.get(fieldType);
                Object targetFieldVal = targetFieldVals.get(fieldType);
                //如果Key是MUL,那么该列的值可以重复, 该列是一个非唯一索引的前导列(第一列)或者是一个唯一性索引的组成部分但是可以含有空值NULL。
                //如果Key是UNI,  那么该列是一个唯一值索引的第一列(前导列),并别不能含有空值(NULL)；
                //处理方式需要放在索引中，这里不做比较
                if (fieldType == ColumnType.Key &&
                        (Objects.equals(sourceFieldVal, "MUL") || Objects.equals(sourceFieldVal, "UNI")
                                || Objects.equals(targetFieldVal, "MUL") || Objects.equals(targetFieldVal, "UNI"))) {
                    continue;
                }

                if (!compareFieldVal(sourceFieldVal, targetFieldVal)) {
                    Map<String, Object> diffFieldVal = new HashMap<>();
                    diffFieldVal.put(SqlUtil.SOURCE, sourceFieldVals);
                    diffFieldVal.put(SqlUtil.TARGET, targetFieldVals);
                    diffTable.put(fieldName, diffFieldVal);
                    break;
                }
            }

        });
        return diffTable;
    }


    public static Map<String, Object> deleteField(Map<String, Map<ColumnType, Object>> source,
                                                  Map<String, Map<ColumnType, Object>> target) {

        Map<String, Object> diffTable = new HashMap<>();
        source.forEach((fieldName, sourceFieldVals) -> {

            /*
             * 对比缺少的字段
             */
            if (!target.containsKey(fieldName)) {
                diffTable.put(fieldName, sourceFieldVals);
            }
        });
        return diffTable;
    }

    /**
     * 以source为主，source中有，target中没有，添加
     * source中没有，target中有，不做操作
     * source中有，target中有，做比对修改操作
     */
    public static Map<String, Map<String, Map<ConstraintNameEnum, String>>> getConstraint(Map<String, Map<String, Map<ConstraintNameEnum, String>>> sourceConstraint,
                                                                                          Map<String, Map<String, Map<ConstraintNameEnum, String>>> targetConstraint) throws SQLException {
        Map<String, Map<String, Map<ConstraintNameEnum, String>>> map = new HashMap<>();
        //循环所有的表
        sourceConstraint.forEach((tableName, constraintNameMap) -> {
            if (!targetConstraint.containsKey(tableName)) {
                map.put(tableName, constraintNameMap);
                return;
            }
            map.put(tableName, getDiffConstraintOfTable(constraintNameMap, targetConstraint.get(tableName)));
        });
        return map;
    }

    private static Map<String, Map<ConstraintNameEnum, String>> getDiffConstraintOfTable(Map<String, Map<ConstraintNameEnum, String>> source,
                                                                                         Map<String, Map<ConstraintNameEnum, String>> target) {
        Map<String, Map<ConstraintNameEnum, String>> map = new HashMap<>();
        source.forEach((constraintName, sourceConstraint) -> {
            if (!target.containsKey(constraintName)) {
                map.put(constraintName, sourceConstraint);
                return;
            }
            Map<ConstraintNameEnum, String> targetConstraint = target.get(constraintName);
            for (ConstraintNameEnum constraintNameEnum : ConstraintNameEnum.diffTypes()) {
                String sourceValue = sourceConstraint.get(constraintNameEnum);
                String targetValue = targetConstraint.get(constraintNameEnum);
                if (!compareFieldVal(sourceValue, targetValue)) {
                    map.put(constraintName, sourceConstraint);
                    break;
                }
            }
        });
        return map;
    }

    /**
     * 以source为主，source中有，target中没有，添加
     * source中没有，target中有，不做操作
     * source中有，target中有，做比对修改操作
     */
    public static Map<String, Map<String, Map<SchemaIndexEnum, String>>> getIndex(Map<String, Map<String, Map<SchemaIndexEnum, String>>> sourceIndex,
                                                                                  Map<String, Map<String, Map<SchemaIndexEnum, String>>> targetIndex) throws SQLException {
        Map<String, Map<String, Map<SchemaIndexEnum, String>>> map = new HashMap<>();
        //循环所有的表
        sourceIndex.forEach((tableName, indexNameMap) -> {
            if (!targetIndex.containsKey(tableName)) {
                //map.put(tableName, indexNameMap);
                return;
            }
            map.put(tableName, getDiffIndexOfTable(indexNameMap, targetIndex.get(tableName)));
        });
        return map;
    }

    private static Map<String, Map<SchemaIndexEnum, String>> getDiffIndexOfTable(Map<String, Map<SchemaIndexEnum, String>> source,
                                                                                 Map<String, Map<SchemaIndexEnum, String>> target) {
        Map<String, Map<SchemaIndexEnum, String>> map = new HashMap<>();
        source.forEach((indexName, sourceIndex) -> {
            if (!target.containsKey(indexName)) {
                map.put(indexName, sourceIndex);
                return;
            }
            Map<SchemaIndexEnum, String> targetIndex = target.get(indexName);
            for (SchemaIndexEnum schemaIndexEnum : SchemaIndexEnum.diffTypes()) {
                String sourceValue = sourceIndex.get(schemaIndexEnum);
                String targetValue = targetIndex.get(schemaIndexEnum);
                if (!compareFieldVal(sourceValue, targetValue)) {
                    map.put(indexName, sourceIndex);
                    break;
                }
            }
        });
        return map;
    }
}
